package com.yuelao.redis;

import com.google.common.collect.Lists;
import com.yuelao.annotation.TaskLock;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;

/**
 * @author FLY
 * @version v1.0
 * @project yuelao-server
 * @date 2017/9/14 11:30
 * @修改记录 版本       修改人         修改时间         修改内容描述
 * --------------------------------------------------
 * <p>
 * --------------------------------------------------
 */
@Component
@Aspect
@Slf4j
public class Lock {
    @Autowired
    private RedisLock redisLock;

    //service切点
    @Pointcut("execution(* com.yuelao.service.*.*(..))")
    private void anyMethod(){}

    //定时切点
    @Pointcut("execution(* com.yuelao.task.*.*(..))")
    private void taskMethod(){}

    //切面应用范围是在切点包下面所有函数
    @Around("anyMethod()")
    public void aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
        String signatureName = joinPoint.getSignature().getName();
        Class<? extends Object> invokeClass = joinPoint.getTarget().getClass();
        // 没有加注解，不拦截
        if (isTagged(invokeClass, signatureName)) {
            if (redisLock.taskLock(signatureName, System.currentTimeMillis())) {
                joinPoint.proceed();
                redisLock.unTaskLock(signatureName);
            }
            return;
        }
        joinPoint.proceed();
    }

    @Around("taskMethod()")
    public void taskAround(ProceedingJoinPoint joinPoint) throws  Throwable {
        String signName = joinPoint.getSignature().getName();
        Class<? extends Object> invokeClass = joinPoint.getTarget().getClass();
        // 没有加注解，不拦截
        if (isTagged(invokeClass, signName)) {
            if (redisLock.taskLock(signName, System.currentTimeMillis())) {
                joinPoint.proceed();
                redisLock.unTaskLock(signName);
            }
            return;
        }
        joinPoint.proceed();
    }



    //扫描父类是否被打上标签,或者父类中的这个方法是否被打上标签
    private boolean isTagged(Class invokeClass, String signatureName) {
        if (isTaggedInInterfaceOf(invokeClass, signatureName)) {
            return true;
        }
        if (!invokeClass.equals(Object.class)) {
            return isTaggedInClassOf(invokeClass, signatureName) ? true :
                    isTagged(invokeClass.getSuperclass(), signatureName);
        }
        return false;
    }

    //扫描当前类的接口
    private boolean isTaggedInInterfaceOf(Class invokeClass, String signatureName) {
        Class[] interfaces = invokeClass.getInterfaces();
        for (Class cas : interfaces) {
            return isTaggedInClassOf(cas, signatureName) ? true :
                    isTaggedInInterfaceOf(cas, signatureName);
        }
        return false;
    }

    //方法名为signatureName的方法tagged有两种情况:方法本身被taged或者方法所在的类被taged
    private boolean isTaggedInClassOf(Class cas, String signatureName) {
       return Lists.newArrayList(cas.getDeclaredMethods())
                .stream().anyMatch(method ->
                        isMethodWithName(method, signatureName) && isMethodTagged(method)
                                || isMethodWithName(method, signatureName) && isClassTagged(cas));
    }

    private boolean isClassTagged(Class invokeClass) {
        return invokeClass.getAnnotation(TaskLock.class) != null;
    }

    private boolean isMethodTagged(Method method) {
        return method.getAnnotation(TaskLock.class) != null;
    }

    private boolean isMethodWithName(Method method, String name) {
        return method.getName().equals(name);
    }
}
